const fs = require("fs");
const path = require("path");
const child_process = require("child_process");
const util = require("util");
const exec = util.promisify(child_process.exec);
const { FFMPEG_PATH } = require("./ffmpeg");
const wav = require("wav");
const { generateASSStyles } = require("./assStyles");
const config = JSON.parse(fs.readFileSync(path.join(__dirname, "../../", "config.json"), "utf8"));

let WaveFile = require("wavefile").WaveFile;

// 假设需要安装的包：wav-reader (pip install类似，但Node: npm install wav-reader)
// 但这里用内置Buffer处理简单WAV分析。实际需npm install wavefile 或类似。

class TxtAndMp32Ass {
  constructor() {
    this.tempWav = "temp.wav";
  }

  // 检查是否为文件路径
  isFilePath(str) {
    // 检查是否包含文件扩展名（如 .txt）
    if (path.extname(str)) return true;

    // 检查是否包含路径分隔符
    if (str.includes("/") || str.includes("\\")) return true;

    // 检查文件是否存在
    try {
      return fs.existsSync(str) && fs.statSync(str).isFile();
    } catch (err) {
      return false;
    }
  }

  // 主方法：生成ASS
  async generateAss(textOrPath, mp3Path, outputPath) {
    try {
      let text = textOrPath;

      // 检查是否为文件路径（简单检查：如果包含扩展名或路径分隔符）
      if (this.isFilePath(textOrPath)) {
        // 如果是文件路径，则读取文件内容
        text = fs.readFileSync(textOrPath, "utf-8");
      }

      // Preprocess text to clean it
      text = text
        .replace(/\r\n/g, "\n") // Normalize line endings
        .replace(/\r/g, "\n") // Convert all line endings to \n
        .replace(/\s+/g, " ") // Replace multiple whitespace with single space
        .replace(/\n+/g, "\n") // Replace multiple newlines with single newline
        .trim();

      await this.convertMp3ToWav(mp3Path);
      const { fs: sampleRate, signal } = await this.readWav(this.tempWav);
      const totalDuration = signal.length / sampleRate;
      console.log(`  Audio info - Sample rate: ${sampleRate} Hz, Signal length: ${signal.length} samples, Duration: ${totalDuration} seconds`);

      // Extract sentences
      const sentences = this.splitTextIntoSentences(text);
      console.log(`  Sentences: ${sentences.length}`);

      // Store sentences for use in timing method
      this.sentencesForTiming = sentences;

      // Generate timings based on reading speed rather than complex audio analysis
      const [matchedStarts, matchedEnds] = this.generateMatchedTimestamps(totalDuration);

      // Log timing information for debugging
      console.log("Generated timings:");
      for (let i = 0; i < Math.min(sentences.length, matchedStarts.length); i++) {
        console.log(`  ${i}: ${matchedStarts[i].toFixed(2)}-${matchedEnds[i].toFixed(2)}: ${sentences[i].substring(0, 30)}...`);
      }

      this.generateAssFile(sentences, matchedStarts, matchedEnds, outputPath);
      fs.unlinkSync(this.tempWav); // 清理
      console.log(`ASS文件已生成: ${outputPath}`);
    } catch (err) {
      console.error("Error:", err);
    }
  }

  // 转换MP3到WAV (需ffmpeg安装)
  async convertMp3ToWav(mp3Path) {
    // 检查ffmpeg是否可用
    try {
      await exec(`"${FFMPEG_PATH}" -version`);
    } catch (error) {
      throw new Error("FFmpeg未安装或不可用。请确保已安装FFmpeg并将其添加到系统PATH中。");
    }

    // 检查输入文件是否存在
    if (!fs.existsSync(mp3Path)) {
      throw new Error(`输入文件不存在: ${mp3Path}`);
    }

    // 执行转换
    try {
      fs.existsSync(this.tempWav) && fs.unlinkSync(this.tempWav);
      const command = `"${FFMPEG_PATH}" -i "${mp3Path}" -acodec pcm_s16le -ar 44100 -ac 1 "${this.tempWav}"`;
      await exec(command);

      // 检查输出文件是否创建成功
      if (!fs.existsSync(this.tempWav)) {
        throw new Error("WAV文件转换失败");
      }
    } catch (error) {
      throw new Error(`音频转换失败: ${error.message}`);
    }
  }

  // 读取WAV (使用wavefile包 if available, otherwise wav package)
  async readWav(wavPath) {
    // Try wavefile package first if available
    if (WaveFile) {
      try {
        console.log("  Using wavefile package to read WAV");
        const wavBuffer = fs.readFileSync(wavPath);
        console.log("  beginning to read WAV ... ");
        const wavFile = new WaveFile(wavBuffer);
        console.log("  ending to read WAV ... ");
        console.log(`  to bit depth conversion ...`);
        wavFile.toBitDepth("32f"); // Convert to 32-bit float
        console.log(` starting to get  sampleRate...`);
        const sampleRate = wavFile.fmt.sampleRate;
        console.log(`  WAV file - Sample rate: ${sampleRate} Hz`);
        console.log(`  beginning to get samples ...`);
        const samples = wavFile.getSamples();
        console.log(`  ending to get samples ...`);
        console.log(` starting to map samples ...`);
        const signal = Array.from(samples).map(v => {
          // process.stdout.write(`  正在处理... : ${v}% \r`);
          return parseFloat(v);
        });
        console.log(`  ending to map samples ...`);
        console.log(`  WAV file - Sample rate: ${sampleRate}, Samples: ${signal.length}`);
        return { fs: sampleRate, signal: signal };
      } catch (err) {
        console.error("Error reading WAV with wavefile package:", err);
      }
    }

    // Fallback to wav package
    console.log("  Using wav package to read WAV");
    const buffer = fs.readFileSync(wavPath);
    const reader = new wav.Reader();

    // Promisify the reader
    return new Promise((resolve, reject) => {
      reader.on("format", format => {
        console.log("  WAV Format:", format);
        reader.sampleRate = format.sampleRate;
      });

      const rawData = [];
      reader.on("data", data => {
        rawData.push(data);
      });

      reader.on("end", () => {
        try {
          // Concatenate all data chunks
          const combinedData = Buffer.concat(rawData);

          // Convert to float32 array (assuming 16-bit audio)
          const signal = new Int16Array(combinedData.buffer, combinedData.byteOffset, combinedData.byteLength / 2);
          const floatSignal = Array.from(signal).map(v => v / 32768); // Normalize to [-1, 1]

          console.log(`  WAV file - Sample rate: ${reader.sampleRate}, Samples: ${floatSignal.length}`);
          resolve({
            fs: reader.sampleRate,
            signal: floatSignal,
          });
        } catch (err) {
          reject(err);
        }
      });

      reader.on("error", err => {
        reject(err);
      });

      // Write the buffer to the reader
      reader.write(buffer);
      reader.end();
    });
  }

  // 分割文本
  splitTextIntoSentences(text) {
    // Clean the text by removing special characters and normalizing whitespace
    text = text
      .replace(/\r\n/g, "\n") // Normalize line endings
      .replace(/\r/g, "\n") // Convert all line endings to \n
      .replace(/\s+/g, " ") // Replace multiple whitespace with single space
      .trim();

    // Split by sentence endings but keep the punctuation with the sentence
    // More sophisticated approach to handle quotes correctly
    let sentences = [];
    let currentSentence = "";

    // Process character by character to properly handle quotes
    for (let i = 0; i < text.length; i++) {
      const char = text[i];
      currentSentence += char;

      // Check if we're at a sentence ending punctuation
      if (/[.!?。！？]/.test(char)) {
        // Look ahead to see if we have closing quotes
        let j = i + 1;
        // Skip whitespace
        while (j < text.length && /\s/.test(text[j])) {
          currentSentence += text[j];
          j++;
        }

        // Check if next non-whitespace character is a closing quote
        if (j < text.length && (text[j] === '"' || text[j] === "”" || text[j] === '"')) {
          // Include the quote in current sentence
          currentSentence += text[j];
          i = j; // Move index past the quote
        }

        // Add completed sentence
        sentences.push(currentSentence.trim());
        currentSentence = "";
      }
    }

    // Handle any remaining text
    if (currentSentence.trim().length > 0) {
      sentences.push(currentSentence.trim());
    }

    // Filter out empty sentences
    sentences = sentences.filter(s => s.length > 0);

    // Additional pass to fix quotes at the beginning of sentences
    for (let i = 1; i < sentences.length; i++) {
      // If current sentence starts with a quote but previous doesn't end with one
      if (
        (sentences[i].startsWith('"') || sentences[i].startsWith("“") || sentences[i].startsWith('"')) &&
        !(sentences[i - 1].endsWith('"') || sentences[i - 1].endsWith("”") || sentences[i - 1].endsWith('"'))
      ) {
        // And if current sentence doesn't end with a quote (it's a starting quote)
        if (!(sentences[i].endsWith('"') || sentences[i].endsWith("”") || sentences[i].endsWith('"'))) {
          // Move the starting quote to the end of the previous sentence
          sentences[i - 1] += '"';
          sentences[i] = sentences[i].substring(1).trim();
        }
      }
    }

    return sentences;
  }

  // 生成匹配时间戳
  generateMatchedTimestamps(totalDuration) {
    const numText = this.sentencesForTiming.length;
    // Evenly distribute subtitles across the total duration

    // Calculate approximate time per character
    const avgCharsPerSec = 15; // Average reading speed: 15 characters per second

    let currentTime = 0;
    const matchedStarts = [];
    const matchedEnds = [];

    for (let i = 0; i < numText; i++) {
      matchedStarts.push(currentTime);

      // Estimate duration based on text length
      const sentence = this.sentencesForTiming[i] || "";
      const estimatedDuration = Math.max(1.5, sentence.length / avgCharsPerSec);

      // But don't exceed the total duration
      const endTime = Math.min(totalDuration, currentTime + estimatedDuration);
      matchedEnds.push(endTime);

      // Next subtitle starts when this one ends
      currentTime = endTime;

      // Add a small gap between subtitles
      currentTime = Math.min(totalDuration, currentTime + 0.2);
    }

    // If we still have time left, stretch the last subtitle to the end
    if (matchedEnds.length > 0 && matchedEnds[matchedEnds.length - 1] < totalDuration) {
      matchedEnds[matchedEnds.length - 1] = totalDuration;
    }

    return [matchedStarts, matchedEnds];
  }

  // 生成ASS文件
  generateAssFile(sentences, starts, ends, outputPath) {
    let assContent = `[Script Info]
Title: Auto Generated ASS
ScriptType: v4.00+

${generateASSStyles(config.projectSettings?.textStyles || {})}
[Events]
Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text`;
    sentences.forEach((sentence, i) => {
      const start = this.formatTime(starts[i]);
      const end = this.formatTime(ends[i]);
      // Clean sentence text and replace line breaks with \N for ASS format
      const formattedSentence = sentence
        .replace(/\r\n/g, "\\N") // Convert Windows line endings
        .replace(/\r/g, "\\N") // Convert Mac line endings
        .replace(/\n/g, "\\N") // Convert Unix line endings
        .replace(/"/g, '"'); // Escape quotes
      if (formattedSentence.trim() !== ".") {
        assContent += `\nDialogue: 0,${start},${end},Current,,0,0,0,,${formattedSentence}`;
      }
    });
    fs.writeFileSync(outputPath, assContent, "utf-8");
  }

  // 格式化时间
  formatTime(seconds) {
    const h = Math.floor(seconds / 3600);
    const m = Math.floor((seconds % 3600) / 60);
    const s = seconds % 60;
    return `${h}:${m.toString().padStart(2, "0")}:${s.toFixed(2).padStart(5, "0")}`;
  }
}

module.exports = TxtAndMp32Ass;

// 使用示例
const generator = new TxtAndMp32Ass();
generator.generateAss("king-of-apes-16.txt", "king-of-apes-16.mp3", "output.ass");
